#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QLabel>
#include <QTimer>
#include <QMessageBox>
#include <QDebug>
#include <QDataStream>
#include <QIODevice>
#include <QTextCodec>
#include <QScrollBar>

static const char *blankString = QT_TRANSLATE_NOOP("Settings", "N/A");

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    intValidator_(new QIntValidator(0, 4000000, this)),
    status_(new QLabel),
    serial_(new QSerialPort(this))
{
    ui->setupUi(this);

    currentTimeLabel = new QLabel(this); // 用于在状态栏显示时间
    ui->statusbar->addPermanentWidget(currentTimeLabel); // 永久显示系统时间
    ui->statusbar->setSizeGripEnabled(true); // 设置是否显示右边的大小控制点
    ui->statusbar->addWidget(status_); // 将状态信息显示到状态栏上

    QTimer *timer = new QTimer(this);
    timer->start(1000); // 每次触发timeout 信号时间间隔为1s
    connect(timer, &QTimer::timeout, this, &MainWindow::_time_update);

    ui->baudRateBox->setInsertPolicy(QComboBox::NoInsert); // 波特率选择
    connect(ui->openSerialPortBtn, &QPushButton::clicked,
            this, &MainWindow::apply); // 应用串口设置
    connect(ui->openSerialPortBtn, &QPushButton::clicked,
            this, &MainWindow::openSerialPort); // 打开串口设置
    connect(ui->serialPortInfoListBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, &MainWindow::showPortInfo);
    connect(ui->serialPortInfoListBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, &MainWindow::checkCustomDevicePathPolicy);
    connect(ui->baudRateBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, &MainWindow::checkCustomBaudRatePolicy);
    fillPortParameters();
    fillPortsInfo();
    updateSettings();

    connect(serial_, &QSerialPort::errorOccurred, this, &MainWindow::handleError);
    connect(serial_, &QSerialPort::readyRead, this, &MainWindow::readData);

    timer_ = new QTimer();
    connect(timer_, &QTimer::timeout, this, &MainWindow::handleTimeout);
}

MainWindow::~MainWindow()
{
    delete ui;
}

/**
 * @brief 时间更新槽函数，用于在状态栏显示当前日期时间
 */
void MainWindow::_time_update()
{
    QDateTime current_time = QDateTime::currentDateTime();
    QString str_time = current_time.toString(" yyyy年MM月dd日 hh:mm:ss "); // 设置显示格式
    currentTimeLabel->setText(str_time);
}

/**
 * @brief 查找系统可用的串口
 */
void MainWindow::scanSerialPort()
{
    foreach (const QSerialPortInfo &info , QSerialPortInfo::availablePorts())
    {
        ui->serialPortInfoListBox->addItem(info.portName());
    }
}

/**
 * @brief 获取当前串口设置
 * @return 当前串口设置值
 */
MainWindow::Settings MainWindow::settings() const
{
    return currentSettings_;
}

/**
 * @brief 显示当前串口的信息
 * @param idx 串口编号
 */
void MainWindow::showPortInfo(int idx)
{
    if (idx == -1) {
        return;
    }

    const QStringList list = ui->serialPortInfoListBox->itemData(idx).toStringList();
    ui->descriptionLabel->setText(tr("Description: %1")
                                  .arg(list.count() > 1 ? list.at(1) : tr(blankString)));
    ui->manufacturerLabel->setText(tr("Manufacturer: %1")
                                   .arg(list.count() > 2 ? list.at(2) : tr(blankString)));
    ui->serialNumberLabel->setText(tr("Serial number: %1")
                                   .arg(list.count() > 3 ? list.at(3) : tr(blankString)));
    ui->locationLabel->setText(tr("Location: %1")
                               .arg(list.count() > 4 ? list.at(4) : tr(blankString)));
    ui->vidLabel->setText(tr("Vendor Identifier: %1")
                          .arg(list.count() > 5 ? list.at(5) : tr(blankString)));
    ui->pidLabel->setText(tr("Product Identifier: %1")
                          .arg(list.count() > 6 ? list.at(6) : tr(blankString)));
}

/**
 * @brief 更新串口参数设置
 */
void MainWindow::apply()
{
    updateSettings();
}

/**
 * @brief 检查用户自定义波特率
 * @param idx 列表编号
 */
void MainWindow::checkCustomBaudRatePolicy(int idx)
{
    // 数据项是否有效
    const bool isCustomBaudRate = !ui->baudRateBox->itemData(idx).isValid();
    ui->baudRateBox->setEditable(isCustomBaudRate); // 是否可以编辑
    if (isCustomBaudRate) // 如果波特率是无效的，编辑有效的波特率
    {
        ui->baudRateBox->clearEditText();
        QLineEdit *edit = ui->baudRateBox->lineEdit();
        edit->setValidator(intValidator_);
    }
}

/**
 * @brief 校验串口是否有效
 * @param idx
 */
void MainWindow::checkCustomDevicePathPolicy(int idx)
{
    const bool isCustomPath = !ui->serialPortInfoListBox->itemData(idx).isValid();
    ui->serialPortInfoListBox->setEditable(isCustomPath);
    if (isCustomPath)
    {
        ui->serialPortInfoListBox->clearEditText();
    }
}

/**
 * @brief 添加串口参数设置
 */
void MainWindow::fillPortParameters()
{
    ui->baudRateBox->addItem(QStringLiteral("9600"), QSerialPort::Baud9600);
    ui->baudRateBox->addItem(QStringLiteral("19200"), QSerialPort::Baud19200);
    ui->baudRateBox->addItem(QStringLiteral("38400"), QSerialPort::Baud38400);
    ui->baudRateBox->addItem(QStringLiteral("115200"), QSerialPort::Baud115200);
    ui->baudRateBox->addItem(tr("Custom"));

    ui->dataBitsBox->addItem(QStringLiteral("5"), QSerialPort::Data5);
    ui->dataBitsBox->addItem(QStringLiteral("6"), QSerialPort::Data6);
    ui->dataBitsBox->addItem(QStringLiteral("7"), QSerialPort::Data7);
    ui->dataBitsBox->addItem(QStringLiteral("8"), QSerialPort::Data8);
    ui->dataBitsBox->setCurrentIndex(3);

    ui->parityBox->addItem(tr("None"), QSerialPort::NoParity);
    ui->parityBox->addItem(tr("Even"), QSerialPort::EvenParity);
    ui->parityBox->addItem(tr("Odd"), QSerialPort::OddParity);
    ui->parityBox->addItem(tr("Mark"), QSerialPort::MarkParity);
    ui->parityBox->addItem(tr("Space"), QSerialPort::SpaceParity);

    ui->stopBitsBox->addItem(QStringLiteral("1"), QSerialPort::OneStop);
#ifdef Q_OS_WIN
    ui->stopBitsBox->addItem(tr("1.5"), QSerialPort::OneAndHalfStop);
#endif
    ui->stopBitsBox->addItem(QStringLiteral("2"), QSerialPort::TwoStop);

    ui->flowControlBox->addItem(tr("None"), QSerialPort::NoFlowControl);
    ui->flowControlBox->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl);
    ui->flowControlBox->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl);
}

/**
 * @brief 补全显示的串口信息
 */
void MainWindow::fillPortsInfo()
{
    ui->serialPortInfoListBox->clear();
    QString description;
    QString manufacturer;
    QString serialNumber;
    const auto infos = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo &info : infos)
    {
        QStringList list;
        description = info.description();
        manufacturer = info.manufacturer();
        serialNumber = info.serialNumber();
        list << info.portName()
             << (!description.isEmpty() ? description : blankString)
             << (!manufacturer.isEmpty() ? manufacturer : blankString)
             << (!serialNumber.isEmpty() ? serialNumber : blankString)
             << info.systemLocation()
             << (info.vendorIdentifier() ?
                     QString::number(info.vendorIdentifier(), 16) : blankString)
             << (info.productIdentifier() ?
                     QString::number(info.productIdentifier(), 16) : blankString);
        ui->serialPortInfoListBox->addItem(list.first(), list);
    }
    ui->serialPortInfoListBox->addItem(tr("Custom"));
}

/**
 * @brief 更新串口设置
 */
void MainWindow::updateSettings()
{
    // 串口名称
    currentSettings_.name = ui->serialPortInfoListBox->currentText();
    // 波特率
    if (ui->baudRateBox->currentIndex() == 4)
    {
        currentSettings_.baudRate = ui->baudRateBox->currentText().toInt();
    }
    else
    {
        currentSettings_.baudRate = static_cast<QSerialPort::BaudRate>(
                    ui->baudRateBox->itemData(ui->baudRateBox->currentIndex()).toInt());
    }
    currentSettings_.stringBaudRate = QString::number(currentSettings_.baudRate);
    // 数据位
    currentSettings_.dataBits = static_cast<QSerialPort::DataBits>(
                ui->dataBitsBox->itemData(ui->dataBitsBox->currentIndex()).toInt());
    currentSettings_.stringDataBits = ui->dataBitsBox->currentText();
    // 校验位
    currentSettings_.parity = static_cast<QSerialPort::Parity>(
                ui->parityBox->itemData(ui->dataBitsBox->currentIndex()).toInt());
    currentSettings_.stringParity = ui->parityBox->currentText();
    // 停止位
    currentSettings_.stopBits = static_cast<QSerialPort::StopBits>(
                ui->stopBitsBox->itemData(ui->stopBitsBox->currentIndex()).toInt());
    currentSettings_.stringStopBits = ui->stopBitsBox->currentText();
    // 流控制
    currentSettings_.flowControl = static_cast<QSerialPort::FlowControl>(
                ui->flowControlBox->itemData(ui->flowControlBox->currentIndex()).toInt());
    currentSettings_.stringFlowControl = ui->flowControlBox->currentText();

    currentSettings_.localEchoEnabled = ui->localEchoCheckBox->isChecked();
}

void MainWindow::handleTimeout()
{
    this->sendTest();
}

/**
 * @brief 打开串口
 */
void MainWindow::openSerialPort()
{
    const Settings p = settings(); // 获取当前串口配置
    serial_->setPortName(p.name);
    serial_->setBaudRate(p.baudRate);
    serial_->setDataBits(p.dataBits);
    serial_->setParity(p.parity);
    serial_->setStopBits(p.stopBits);
    serial_->setFlowControl(p.flowControl);
    if (serial_->open(QIODevice::ReadWrite))
    {
        showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6")
                          .arg(p.name, p.stringBaudRate, p.stringDataBits,
                               p.stringParity, p.stringStopBits, p.stringFlowControl));
    }
    else
    {
        QMessageBox::critical(this, tr("Error"), serial_->errorString());
        showStatusMessage(tr("Open error"));
    }
}

/**
 * @brief 关闭串口
 */
void MainWindow::closeSerialPort()
{
    if (serial_->isOpen())
    {
        serial_->close();
    }
    showStatusMessage(tr("Disconnected"));
}

/**
 * @brief 向串口写入数据
 * @param data
 */
void MainWindow::writeData(const QByteArray &data)
{
    serial_->write(data);
}

/**
 * @brief 从串口读取数据
 */
void MainWindow::readData()
{
    const QByteArray data = serial_->readAll(); // 读取串口数据

    ui->consoleText->insertPlainText(data);
    QScrollBar *bar = QScrollBar::verticalScrollBar();
    bar->setValue(bar->maximum());
}

/**
 * @brief 处理错误
 * @param error
 */
void MainWindow::handleError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError)
    {
        QMessageBox::critical(this, tr("Critical Error"), serial_->errorString());
        closeSerialPort();
    }
}

/**
 * @brief 设置状态栏显示的信息
 * @param message
 */
void MainWindow::showStatusMessage(const QString &message)
{
    status_->setText(message);
}

void MainWindow::sendTest()
{
    this->writeData("hello serial");
}

void MainWindow::readTest()
{
    QByteArray data = serial_->readAll();
    ui->consoleText->insertPlainText(data);
    this->writeData(data);
}

